﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.ADF;
using System.Reflection;
using PlanningGIS.Framework;

using System.Runtime.InteropServices;
using System.IO;
using System.Xml;
using PlanningGIS.ArcGIS;
using PlanningGIS.Util.Log;

namespace PlanningGIS.CoordCovert.model
{
    /// <summary>
    /// 六参数结构
    /// </summary>
    public struct Parameter6
    {
        public double X0;
        public  double Y0;
        public double x0;
        public double y0;
        public double c;
        public double d;
    }

   
    public class AffineConvert6Parameters : CoordConvertParameters
    {
        //private List<IFeatureClass> pFeatClses;

        public double X0;
        public double Y0;
        public double x0;
        public double y0;
        public double c;
        public double d;

        private object missing = Type.Missing;
        ///// <summary>
        ///// 需要进行转换的要素类集合
        ///// </summary>
        //public List<IFeatureClass> FeatClses
        //{
        //    set { pFeatClses = value; }
        //}

        public AffineConvert6Parameters()
        {
            X0 = 0;
            Y0 = 0;
            x0 = 0;
            y0 = 0;
            c = 1;
            d = 0;

        }

        /// <summary>
        /// 数据转换
        /// </summary>
        /// <returns></returns>
        public override bool Operate()
        {
            if (pFeatClses == null || pFeatClses.Count == 0)
            {
               LogHelper.WriteErrLog("请设置需要进行仿射变换的数据！");
                return false;
            }

            setProcessBarMax.Invoke(pFeatClses.Count);

            int countTotal = 0;
            int errorCountTotal = 0;
            int rightCountTotal = 0;

            //bool flag;//true表示用自定义算法转换，false表示用ArcGIS的空间校正工具进行转换
            //flag = false;

            try
            {

                for (int i = 0; i < pFeatClses.Count; i++)
                {
                    if (stopAnyway)
                        return false;

                    setProcessBarCurrent.Invoke(i + 1);
                    IFeatureClass pFeatCls = pFeatClses[i];

                    try
                    {
                        int allCount = pFeatCls.FeatureCount(null);
                        if (allCount == 0) continue;

                        setSubProcessBarMax(allCount);

                        LogHelper.WriteLog("开始对数据[" + pFeatCls.AliasName + "]进行六参数变换...");

                        #region 逐要素转换

                        IFeatureCursor pFeatCur = pFeatCls.Update(null, false);
                        IFeature pFeature = pFeatCur.NextFeature();

                        int count = 0;
                        int rightCount = 0;
                        int errorCount = 0;

                        while (pFeature != null)
                        {
                            if (stopAnyway)
                                return false;

                            count++;
                            SetSubProcessBarCurrent.Invoke(count);

                            if (ConvertFeature(pFeature))
                            {
                                rightCount++;
                            }
                            else
                            {
                                errorCount++;
                            }

                            pFeatCur.UpdateFeature(pFeature);
                            pFeature = pFeatCur.NextFeature();
                        }

                        if (errorCount == 0)
                        {
                            LogHelper.WriteLog("[" + pFeatCls.AliasName + "]数据转换完毕。待转换总数：" + count + "；成功：" + rightCount + "；失败：" + errorCount + "\n", zjgisLogType.zjgisLogDone);
                        }
                        else
                        {
                            LogHelper.WriteLog("[" + pFeatCls.AliasName + "]数据转换完毕。待转换总数：" + count + "；成功：" + rightCount + "；失败：" + errorCount + "\n", zjgisLogType.zjgisLogError);
                        }

                        countTotal += count;
                        rightCountTotal += rightCount;
                        errorCountTotal += errorCount;

                        Marshal.ReleaseComObject(pFeatCur);

                        #endregion

                        //AddFeatureclassSpatialIndex(pFeatCls, 0, 0, 0);
                    }
                    catch (Exception ex)
                    {
                        LogHelper.WriteErrLog("转换数据[" + pFeatCls.AliasName + "]失败：" + ex.Message);

                    }
                }

                if (errorCountTotal == 0)
                {
                    LogHelper.WriteDoneLog("所有数据转换完毕。待转换总数：" + countTotal + "；成功：" + rightCountTotal + "；失败：" + errorCountTotal + "\n");
                }
                else
                {
                   LogHelper.WriteErrLog("所有数据转换完毕。待转换总数：" + countTotal + "；成功：" + rightCountTotal + "；失败：" + errorCountTotal + "\n");
                }

                return true;
            }
            catch (Exception ex)
            {
               LogHelper.WriteErrLog("转换失败：" + ex.Message);
                return false;
            }
        }

        #region private functions

        /// <summary>
        /// 对单个要素进行转换
        /// </summary>
        /// <param name="pFeature"></param>
        /// <returns>转换成功返回true、否则返回false</returns>
        private bool ConvertFeature(IFeature pFeature)
        {
            IGeometry pGeometry = pFeature.ShapeCopy;
            if (pGeometry.IsEmpty) return true;     //如果图形为空，则无需转换

            try
            {
                if (pGeometry.GeometryType == esriGeometryType.esriGeometryMultipoint ||
                    pGeometry.GeometryType == esriGeometryType.esriGeometryPolyline)
                {
                    IPointCollection pPoints = pGeometry as IPointCollection;
                    for (int i = 0; i < pPoints.PointCount; i++)
                    {
                        IPoint pPoint = pPoints.get_Point(i);

                        ConvertPoint(pPoint);

                        pPoints.UpdatePoint(i, pPoint);
                    }
                }
                //如果是面状数据，因为首尾点是同一个点，第一个点做了坐标转换后，最后一个点是不需要再进行转换的，否则该点（首/尾点）就会转换了两次
                else if (pGeometry.GeometryType == esriGeometryType.esriGeometryPolygon)
                {
                    #region 对于多part的数据，无法转换
                    //IGeometryCollection pGeometries = pGeometry as IGeometryCollection;
                    //if (pGeometries.GeometryCount != 1)
                    //{
                    //   LogHelper.WriteErrLog("ID为[" + pFeature.OID.ToString() + "]的要素为非单PART图形，目前程序无法进行转换，请您手工操作！");
                    //    return false;
                    //}

                    //IPointCollection pPoints = pGeometry as IPointCollection;
                    //for (int i = 0; i < pPoints.PointCount - 1; i++)
                    //{
                    //    IPoint pPoint = pPoints.get_Point(i);

                    //    ConvertPoint(pPoint);

                    //    pPoints.UpdatePoint(i, pPoint);
                    //}
                    #endregion

                    #region 用以下方式转换后的数据图形都看不到了
                    //IGeometryCollection pGeometries = pGeometry as IGeometryCollection;
                    //if (pGeometries == null || pGeometries.GeometryCount == 0) return true;

                    //for (int i = 0; i < pGeometries.GeometryCount; i++)
                    //{
                    //    IPointCollection pPoints = pGeometries.get_Geometry(i) as IPointCollection;

                    //    //最后一个点不用转！
                    //    for (int j = 0; j < pPoints.PointCount - 1; j++)
                    //    {
                    //        IPoint pPoint = pPoints.get_Point(j);

                    //        ConvertPoint(pPoint);

                    //        pPoints.UpdatePoint(j, pPoint);
                    //    }
                    //}
                    #endregion

                    #region 可以较好的转换面图层，但是Z值信息会丢失，转换后全为零
                    IGeometryCollection pGeometryNew = new GeometryBagClass();
                    IPolygon pPolygon = null;
                    ITopologicalOperator2 pTop = null;

                    IGeometryCollection pGeometries = pGeometry as IGeometryCollection;
                    if (pGeometries == null || pGeometries.GeometryCount == 0) return true;

                    bool hasZ = false;
                    IFeatureClass pFeatCls = (pFeature.Class as IFeatureClass);
                    IField pField = pFeatCls.Fields.get_Field(pFeatCls.FindField(pFeatCls.ShapeFieldName));
                    IFieldEdit pFieldEdit = pField as IFieldEdit;
                    IGeometryDef pGeometryDef = pFieldEdit.GeometryDef;
                    hasZ = pGeometryDef.HasZ;

                    for (int i = 0; i < pGeometries.GeometryCount; i++)
                    {
                        IGeometry pGeomLow = pGeometries.get_Geometry(i);
                        IGeometry pGeomHigh = Convertor.ConvertLow2High(pGeomLow);

                        if (hasZ)
                        {
                            pGeomHigh = GeometryHelper.Geom2Dto3D(pGeomHigh);
                        }

                        IPointCollection pPoints = pGeomHigh as IPointCollection;

                        //最后一个点不用转！
                        for (int j = 0; j < pPoints.PointCount - 1; j++)
                        {
                            IPoint pPoint = pPoints.get_Point(j);

                            ConvertPoint(pPoint);

                            pPoints.UpdatePoint(j, pPoint);
                        }

                        if (pPolygon == null)
                        {
                            pPolygon = pGeomHigh as IPolygon;
                            pTop = pPolygon as ITopologicalOperator2;
                        }
                        else
                        {
                            pTop.IsKnownSimple_2 = false;
                            if (!pTop.IsKnownSimple) pTop.Simplify();

                            ITopologicalOperator2 pTopTemp = pGeomHigh as ITopologicalOperator2;
                            pTopTemp.IsKnownSimple_2 = false;
                            if (!pTopTemp.IsKnownSimple) pTopTemp.Simplify();

                            pPolygon = pTop.Union(pGeomHigh) as IPolygon;
                            pTop = pPolygon as ITopologicalOperator2;
                        }
                    }

                    pFeature.Shape = pPolygon;
                    #endregion
                }
                else if (pGeometry.GeometryType == esriGeometryType.esriGeometryPoint)
                {
                    IPoint pPoint = pGeometry as IPoint;

                    ConvertPoint(pPoint);
                }
                else
                {
                   LogHelper.WriteErrLog("类型不支持！");
                    return false;
                }

                pFeature.Shape = pGeometry;
                pFeature.Store();

                return true;
            }
            catch (Exception ex)
            {
               LogHelper.WriteErrLog("对ID为[" + pFeature.OID.ToString() + "]的要素转换失败：" + ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 对某个点进行转换
        /// </summary>
        /// <param name="pPoint"></param>
        private void ConvertPoint(IPoint pPoint)
        {
            double x = pPoint.X;
            double y = pPoint.Y;

            double X, Y;

            X = X0 + c * (x - x0) + d * (y - y0);
            Y = Y0 - d * (x - x0) + c * (y - y0);

            pPoint.X = X;
            pPoint.Y = Y;

        }


        /// <summary>
        /// 删除要素类的空间索引
        /// </summary>
        /// <param name="pFeatureClass"></param>
        /// <returns></returns>
        private bool DeleteFeatureclassSpatialIndex(IFeatureClass pFeatureClass)
        {
            // Get an enumerator for indexes based on the shape field.
            IIndexes indexes = pFeatureClass.Indexes;
            string shapeFieldName = pFeatureClass.ShapeFieldName;
            IEnumIndex enumIndex = indexes.FindIndexesByFieldName(shapeFieldName);
            enumIndex.Reset();

            // Get the index based on the shape field (should only be one) and delete it.
            IIndex index = enumIndex.Next();

            if (index != null)
            {
                pFeatureClass.DeleteIndex(index);
            }

            return true;
        }

        /// <summary>
        /// 添加要素类的空间索引
        /// </summary>
        /// <param name="pFeatureClass"></param>
        /// <param name="pGridOneSize"></param>
        /// <param name="pGridTwoSize"></param>
        /// <param name="pGridThreeSize"></param>
        /// <returns></returns>
        private bool AddFeatureclassSpatialIndex(IFeatureClass pFeatureClass, Double gridOneSize, Double gridTwoSize, Double gridThreeSize)
        {
            // Clone the shape field from the feature class.
            string shapeFieldName = pFeatureClass.ShapeFieldName;
            int shapeFieldIndex = pFeatureClass.FindField(shapeFieldName);
            IFields fields = pFeatureClass.Fields;
            IField sourceField = fields.get_Field(shapeFieldIndex);
            IClone sourceFieldClone = (IClone)sourceField;
            IClone targetFieldClone = sourceFieldClone.Clone();
            IField targetField = (IField)targetFieldClone;

            // Open the geometry definition from the cloned field and modify it.
            IGeometryDef geometryDef = targetField.GeometryDef;
            IGeometryDefEdit geometryDefEdit = (IGeometryDefEdit)geometryDef;
            geometryDefEdit.GridCount_2 = 3;
            geometryDefEdit.set_GridSize(0, gridOneSize);
            geometryDefEdit.set_GridSize(1, gridTwoSize);
            geometryDefEdit.set_GridSize(2, gridThreeSize);

            // Create a spatial index and set the required attributes.
            IIndex newIndex = new IndexClass();
            IIndexEdit newIndexEdit = (IIndexEdit)newIndex;
            newIndexEdit.Name_2 = shapeFieldName + "_Index";
            newIndexEdit.IsAscending_2 = true;
            newIndexEdit.IsUnique_2 = false;

            // Create a fields collection and assign it to the new index.
            IFields newIndexFields = new FieldsClass();
            IFieldsEdit newIndexFieldsEdit = (IFieldsEdit)newIndexFields;
            newIndexFieldsEdit.AddField(targetField);
            newIndexEdit.Fields_2 = newIndexFields;

            // Add the spatial index back into the feature class.
            pFeatureClass.AddIndex(newIndex);

            return true;
        }

        #endregion
    }
}
